/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  Convert.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2015/03/25
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/

#include "string_convert.h"

#include <locale.h>

#include "../sync/lock.h"

#ifndef WIN32
#include "stringconv_posix.h"
#endif

namespace qb {
namespace base {

CConvertFast::CConvertFast()
    : m_str_len(0),
      m_wstr_len(0),
      m_str(new CharBuf()),
      m_wstr(new WideBuf()) {}

CConvertFast::~CConvertFast() {
  if (m_str) delete m_str;
  if (m_wstr) delete m_wstr;
}
const char* CConvertFast::ToUtf8(const wchar_t* pwstr, int len) {
  m_str->clear();
#ifdef WIN32
  // 宽字符转化为utf8编码,必须精确估计utf8字符串长度
  len = (pwstr && len < 0) ? (wcslen(pwstr)) : len;
  int ulen =
      pwstr ? WideCharToMultiByte(CP_UTF8, 0, pwstr, len, NULL, 0, NULL, NULL)
            : 0;
  m_str->Add((char)0, ulen + 1);
  char* pcstr = m_str->GetBuffer();
  m_str_len = (pwstr && len > 0)
                  ? WideCharToMultiByte(CP_UTF8 /*CP_OEMCP*/, NULL, pwstr, len,
                                        pcstr, ulen + 1, NULL, false)
                  : 0;
  return pcstr;
#else
  if (!pwstr || len <= 0) {
    return "";
  }

  USES_CONVERSION;
  std::wstring str(pwstr, len);
  char* res = WCHAR_TO_UTF8(str.c_str());
  if (res) {
    m_str_len = strlen(res);
    m_str->Add((char)0, m_str_len + 1);
    strncpy(m_str->GetBuffer(), res, m_str_len);
  }
  return m_str->GetBuffer();
#endif
}
const char* CConvertFast::ToUtf8(const char* gb2312, int len) {
  const wchar_t* wstr = ToWide(gb2312, len);
  return ToUtf8(wstr, GetWstrSize());
}
const char* CConvertFast::ToGbk(const char* utf8, int inlen) {
  // 性能测试
  // 测试数据:"中华人民共和国",100000次
  // 旧版:35961us
  // 新版:30387us
  // 对比性能提升了18%左右
  // 新版:优化了Add,平均时间23856,提升了50%
  const wchar_t* wstr = UToWide(utf8, inlen);
  return ToGbk(wstr, GetWstrSize());
}
namespace {
bool CheckUtf8Ascii(const char* utf8, int inlen, int& asciiLength) {
  if (inlen > 0) {
    for (int i = 0; i < inlen; i++) {
      if (utf8[i] < 0 || utf8[i] >= 127) return false;
    }
    asciiLength = inlen;
    return true;
  }

  int i = 0;
  const int nmax = 256;  // 最大检测前面256个字符
  for (; i < nmax && utf8[i] != 0; i++) {
    if (utf8[i] < 0 || utf8[i] >= 127) return false;
  }
  if (utf8[i] == 0) {
    asciiLength = i;
    return true;
  }
  return false;
}
}  // namespace

const char* CConvertFast::ToGbkEx(const char* utf8, int inlen)  // utf8==>gbk
{
  // 如果utf8都是ascii,那么无需转化
  int asciiLength = 0;
  m_str->clear();
  if (utf8 && CheckUtf8Ascii(utf8, inlen, asciiLength)) {
    m_str->append(utf8, asciiLength);
  }
#ifdef WIN32
  const wchar_t* wstr = UToWide(utf8, inlen);
  return ToGbk(wstr, GetWstrSize());
#else
  if (!utf8 || inlen <= 0) {
    return "";
  }

  USES_CONVERSION;
  std::string str(utf8, inlen);
  char* res = UTF8_TO_GBK(str.c_str());
  if (res) {
    m_str_len = strlen(res);
    m_str->Add((char)0, m_str_len + 1);
    strncpy(m_str->GetBuffer(), res, m_str_len);
  }
#endif
  return m_str->GetBuffer();
}
const char* CConvertFast::ToGbkTruncate(int capcity, const char* utf8,
                                        int inlen, bool comma) {
#ifdef WIN32
  // 将utf8转化成gbk时,考虑gbk缓冲区的容量来截断,不会导致乱码
  const wchar_t* pwstr = UToWide(utf8, inlen);
  int len = GetWstrSize();
  m_str_len = 0;
  m_str->clear();
  m_str->Add((char)0, len + len + 1);
  char* pcstr = m_str->GetBuffer();
  if (pwstr && len > 0) {
    class ConvertUtil {
     public:
      static int DoConvert(const wchar_t* src, int srclen, char* dest,
                           int capcity, bool& truncate) {
        if (srclen <= 0) return 0;
        int nexpect = WideCharToMultiByte(936 /*CP_OEMCP*/, NULL, src, srclen,
                                          NULL, 0, NULL, false);
        if (nexpect < capcity)
          return WideCharToMultiByte(936 /*CP_OEMCP*/, NULL, src, srclen, dest,
                                     capcity, NULL, false);
        int len = DoConvert(src, srclen / 2, dest, capcity, truncate);
        if (len <= 0) {
          truncate = true;
          return 0;
        }
        len += DoConvert(src + srclen / 2, srclen - srclen / 2, dest + len,
                         capcity - len, truncate);
        return len;
      }
    };
    capcity = (capcity - 1 > len + len) ? (len + len) : (capcity - 1);
    if (comma) capcity -= 3;
    bool truncate = false;
    m_str_len = ConvertUtil::DoConvert(pwstr, len, pcstr, capcity, truncate);
    if (comma && truncate) {
      memcpy(pcstr + m_str_len, "...", 3);
      m_str_len += 3;
    }
  }
  return pcstr;
#else
  return nullptr;
#endif
}

const char* CConvertFast::ToGbk(const wchar_t* pwstr, int len) {
  m_str->clear();
  len = (pwstr && len < 0) ? (wcslen(pwstr)) : len;
  m_str->Add((char)0, len + len + 1);
#ifdef WIN32
  // gbk编码的长度与宽字符编码的长度的关系
  // len_gbk < len_wide*2
  // 相对于之前通过WideCharToMultiByte 来计算长度,有16%~24%的性能提升
  char* pcstr = m_str->GetBuffer();
  m_str_len = (pwstr && len > 0)
                  ? WideCharToMultiByte(936 /*CP_OEMCP*/, NULL, pwstr, len,
                                        pcstr, len + len + 1, NULL, false)
                  : 0;
#else
  if (!pwstr || len <= 0) {
    return "";
  }

  USES_CONVERSION;
  std::wstring str(pwstr, len);
  char* res = WCHAR_TO_GBK(str.c_str());
  if (res) {
    m_str_len = strlen(res);
    strncpy(m_str->GetBuffer(), res, len * 2);
  }
#endif
  return m_str->GetBuffer();
}
const wchar_t* CConvertFast::UToWide(const char* utf8, int len) {
  m_wstr->clear();
  len = (utf8 && len < 0) ? (strlen(utf8)) : len;
  m_wstr->Add((wchar_t)0, len + len + 1);
#ifdef WIN32
  // utf8编码的长度与宽字符编码的长度的关系
  // len_utf8 < len_wide*2
  // 如果len为-1,那么MultiByteToWideChar返回值是宽字符长度+1
  // 如果len为正数,那么MultiByteToWideChar返回值是宽字符长度
  // 经过测试,对字符串"中华人民共和国",100000次,性能提升了一倍左右
  // 两遍MultiByteToWideChar调用,24701us
  // 一遍MultiByteToWideChar调用,13871us

  wchar_t* wstr = m_wstr->GetBuffer();
  m_wstr_len = (utf8 && len > 0) ? MultiByteToWideChar(CP_UTF8, 0, utf8, len,
                                                       wstr, len + len + 1)
                                 : 0;
#else
  if (!utf8 || len <= 0) {
    return L"";
  }

  USES_CONVERSION;
  std::string str(utf8, len);
  wchar_t* res = UTF8_TO_WCHAR(str.c_str());
  if (res) {
    m_wstr_len = wcslen(res);
    wcsncpy(m_wstr->GetBuffer(), res, len * 2);
  }
#endif
  return m_wstr->GetBuffer();
}
const wchar_t* CConvertFast::ToWide(const char* gbk, int len) {
  m_wstr->clear();
  len = (gbk && len < 0) ? (strlen(gbk)) : len;
  m_wstr->Add((wchar_t)0, len + 1);
#ifdef WIN32
  // gbk编码的长度与宽字符编码的长度的关系
  // len_gbk < len_wide*2
  // 如果len为-1,那么MultiByteToWideChar返回值是宽字符长度+1
  // 如果len为正数,那么MultiByteToWideChar返回值是宽字符长度

  wchar_t* wBuf = m_wstr->GetBuffer();
  m_wstr_len = (gbk && len > 0)
                   ? MultiByteToWideChar(936 /*CP_ACP*/, 0, (const char*)gbk,
                                         len, wBuf, len + 1)
                   : 0;
#else
  if (!gbk || len <= 0) {
    return L"";
  }

  USES_CONVERSION;
  std::string str(gbk, len);
  wchar_t* res = GBK_TO_WCHAR(str.c_str());
  if (res) {
    m_wstr_len = wcslen(res);
    wcsncpy(m_wstr->GetBuffer(), res, len);
  }
#endif
  return m_wstr->GetBuffer();
}
int CConvertFast::GetWstrSize(const char* gbk, int len) {
  ToWide(gbk, len);
  return GetWstrSize();
}
int CConvertFast::GetStrSize(const wchar_t* pwstr, int len) {
  ToGbk(pwstr, len);
  return GetStrSize();
}
////////////////////////////////////////////////////////////////////////////////
bool FastToWideString(const char* buffer, WideString& szRet) {
#ifdef WIN32
  static wchar_t wbuf[256] = {0};
  size_t wlen = MultiByteToWideChar(936 /*CP_ACP*/, 0, (const char*)buffer, -1,
                                    wbuf, 256);
  if (wlen > 0) {
    wbuf[wlen] = wchar_t(0);
    szRet = wbuf;
    return true;
  }
  szRet = L"";
#else
  USES_CONVERSION;
  wchar_t* out_ptr = GBK_TO_WCHAR(buffer);
  szRet = out_ptr;

  return true;
#endif
  return false;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class CConvert : protected CConvertFast {  // 多线程版本
  CSLock m_fast_lock;

 public:
  CConvert() {}
  WideString ToWideString(const char* gbk, int len);
  void SingleGbkToWide(const char* gbk, wchar_t& wc);
  void SingleWideToGbk(const wchar_t& wc, char* gbk);
  int G2WSize(const char* gbk, int len);
  int W2GSize(const wchar_t* pwstr, int len);

  WideString G2W(const char* gbk, int len);
  BaseString W2G(const wchar_t* pwstr, int len = -1);
  BaseString G2U(const BaseString& gb2312);
  BaseString U2G(const BaseString& utf8);
};
WideString CConvert::ToWideString(const char* gbk, int len) {
  SCOPED_LOCK(m_fast_lock);
  WideString wstr = ToWide(gbk, len);
  return wstr;
}
void CConvert::SingleGbkToWide(const char* gbk, wchar_t& wc) {
  SCOPED_LOCK(m_fast_lock);
  const wchar_t* wstr = ToWide(gbk, 2);
  wc = *wstr;
}
void CConvert::SingleWideToGbk(const wchar_t& wc, char* gbk) {
  SCOPED_LOCK(m_fast_lock);
  const char* str = ToGbk(&wc, 1);
  memcpy(gbk, str, 2);
}
int CConvert::G2WSize(const char* gbk, int len) {
  SCOPED_LOCK(m_fast_lock);
  ToWide(gbk, len);
  return GetWstrSize();
}
int CConvert::W2GSize(const wchar_t* pwstr, int len) {
  SCOPED_LOCK(m_fast_lock);
  ToGbk(pwstr, len);
  return GetStrSize();
}
WideString CConvert::G2W(const char* gbk, int len) {
  SCOPED_LOCK(m_fast_lock);
  WideString wstr = ToWide(gbk, len);
  return wstr;
}
BaseString CConvert::W2G(const wchar_t* pwstr, int len) {
  SCOPED_LOCK(m_fast_lock);
  BaseString gbk = ToGbk(pwstr, len);
  return gbk;
}
BaseString CConvert::G2U(const BaseString& gb2312) {
  SCOPED_LOCK(m_fast_lock);
  BaseString utf8 = ToUtf8(gb2312.c_str());
  return utf8;
}
BaseString CConvert::U2G(const BaseString& utf8) {
  SCOPED_LOCK(m_fast_lock);
  BaseString gbk = ToGbk(utf8.c_str());
  return gbk;
}
//////////////////////////////////////////////////////////////////////////
static CConvert& GetConverter() {
  static CConvert gwConverter;
  return gwConverter;
}
//////////////////////////////////////////////////////////////////////////
vector<pair<BaseString, double>>& U2G(vector<pair<BaseString, double>>& utf8) {
  for (vector<pair<BaseString, double>>::iterator it = utf8.begin();
       it != utf8.end(); ++it) {
    it->first = U2G(it->first.c_str());
  }
  return utf8;
}
vector<pair<BaseString, BaseString>>& U2G(
    vector<pair<BaseString, BaseString>>& utf8) {
  for (vector<pair<BaseString, BaseString>>::iterator it = utf8.begin();
       it != utf8.end(); ++it) {
    it->first = U2G(it->first.c_str());
    it->second = U2G(it->second.c_str());
  }
  return utf8;
}
vector<BaseString>& U2G(vector<BaseString>& utf8) {
  for (vector<BaseString>::iterator it = utf8.begin(); it != utf8.end(); ++it) {
    *it = U2G(it->c_str());
  }
  return utf8;
}

BaseString G2U(const BaseString& gb2312) { return GetConverter().G2U(gb2312); }
BaseString U2G(const BaseString& utf8) { return GetConverter().U2G(utf8); }
BaseString G2J(const BaseString& str) {
  BaseString out;
  int nlen = str.size();
  for (int i = 0; i < nlen; i++) {
    char c = str.at(i);
    if (c == '\\' || c == '"') {
      out.push_back('\\');
      out.push_back(c);
    } else if (c == '\r') {
      out.push_back('\\');
      out.push_back('r');
    } else if (c == '\n') {
      out.push_back('\\');
      out.push_back('n');
    } else
      out.push_back(c);
  }
  bool set = out.size() != str.size();
  return out;
}
BaseString J2G(const BaseString& str) {
  BaseString out;
  // 从左到右G2J,从右到左J2G
  //	\	\\
				//	"	\"
  //	\r	\\r
  //	\n	\\n
  int nlen = str.size();
  for (int i = 0; i < nlen; i++) {
    char c = str.at(i);
    if (c == '\\' && i + 1 < nlen) {
      char n = str.at(i + 1);
      if (n == 'n') {
        out.push_back('\n');
        i++;
      } else if (n == 'r') {
        out.push_back('\r');
        i++;
      } else if (n == '\\' || n == '"') {
        out.push_back(n);
        i++;
      } else
        out.push_back(c);
    } else
      out.push_back(c);
  }
  return out;
}
BaseString G2UT(const BaseString& gbk) {
#ifdef WIN32
  const int bufsize = 1024;
  wchar_t wbuf[bufsize] = {0};
  const int ssize = 1024;
  char sbuf[ssize] = {0};

  int len = MultiByteToWideChar(CP_ACP, 0, gbk.c_str(), gbk.size(), NULL, 0);
  wchar_t* wstr = (len + 1) < bufsize ? wbuf : (new wchar_t[len + 1]);
  memset(wstr, 0, sizeof(wchar_t) * (len + 1));
  MultiByteToWideChar(CP_ACP, 0, gbk.c_str(), gbk.size(), wstr, len);

  int mlen = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
  char* str = (mlen + 1) < ssize ? sbuf : (new char[mlen + 1]);
  memset(str, 0, sizeof(char) * (mlen + 1));
  WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, mlen, NULL, NULL);

  BaseString ustr = str;
  if (wstr != wbuf) {
    delete[] wstr;
  }
  if (str != sbuf) {
    delete[] str;
  }
  return ustr;
#else
  BaseString ustr;
  return ustr;
#endif
}

BaseString U2GT(const BaseString& utf8) {
#ifdef WIN32
  const int bufsize = 1024;
  wchar_t wbuf[bufsize] = {0};
  const int ssize = 1024;
  char sbuf[ssize] = {0};

  int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), NULL, 0);
  wchar_t* wstr = (len + 1) < bufsize ? wbuf : (new wchar_t[len + 1]);
  memset(wstr, 0, sizeof(wchar_t) * (len + 1));
  MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size(), wstr, len);
  int mlen = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
  char* str = (mlen + 1) < ssize ? sbuf : (new char[mlen + 1]);
  memset(str, 0, sizeof(char) * (mlen + 1));
  WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, mlen, NULL, NULL);

  BaseString gbk = str;
  if (wstr != wbuf) {
    delete[] wstr;
  }
  if (str != sbuf) {
    delete[] str;
  }
  return gbk;
#else
  BaseString gbk;
  return gbk;
#endif
}
////////////////////////////////////////////////////////////////////////////////
// 在utf8和WideString之间转化
BaseString W2U(const wchar_t* pwstr, int len) {
#ifdef WIN32
  DWORD dwNum = WideCharToMultiByte(CP_UTF8 /*CP_OEMCP*/, NULL, pwstr, len,
                                    NULL, 0, NULL, false);
  if (dwNum <= 0) return "";
  char* pcstr = new char[dwNum + 1];  // 容易有内存碎片
  memset(pcstr, 0, sizeof(char) * (dwNum + 1));
  WideCharToMultiByte(CP_UTF8 /*CP_OEMCP*/, NULL, pwstr, len, pcstr, dwNum,
                      NULL, false);
#else
  std::wstring strIn(pwstr, len);
  USES_CONVERSION;
  char* pcstr = WCHAR_TO_UTF8(strIn.c_str());
#endif

  BaseString str = pcstr;
  delete[] pcstr;
  return str;
}
WideString U2W(const char* utf8, int len) {
#ifdef WIN32
  size_t wlen = MultiByteToWideChar(CP_UTF8 /*CP_ACP*/, 0, (const char*)utf8,
                                    len, NULL, 0);
  if (wlen <= 0) return L"";
  wchar_t* wBuf = new wchar_t[wlen + 1];
  memset(wBuf, 0, (wlen + 1) * sizeof(wchar_t));
  // 使用MB_PRECOMPOSED | MB_ERR_INVALID_CHARS作为第二个参数
  // 可以检测出utf8是否是真正的utf8编码
  int nret = MultiByteToWideChar(CP_UTF8 /*CP_ACP*/, 0, (const char*)utf8, len,
                                 wBuf, int(wlen + 1));
#else
  if (!utf8 || len <= 0) {
    return L"";
  }
  std::string strIn(utf8, len);
  USES_CONVERSION;
  wchar_t* wBuf = UTF8_TO_WCHAR(strIn.c_str());
#endif
  WideString szRet = wBuf;
  delete[] wBuf;
  return szRet;
}
//////////////////////////////////////////////////////////////////////////

void G2WC(const char* gbk, wchar_t& wc) {
  GetConverter().SingleGbkToWide(gbk, wc);
}
void W2GC(const wchar_t& wc, char* gbk) {
  GetConverter().SingleWideToGbk(wc, gbk);
}
WideString G2W(const char* gbk, int len) {
  return GetConverter().G2W(gbk, len);
}
int G2WSize(const char* gbk, int len) {
  return GetConverter().G2WSize(gbk, len);
}
BaseString W2G(const wchar_t* pwstr, int len) {
  return GetConverter().W2G(pwstr, len);
}
int W2GSize(const wchar_t* pwstr, int len) {
  return GetConverter().W2GSize(pwstr, len);
}
}  // namespace base
}  // namespace qb
